#include "appkit/application.h"

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include <map>
#include <vector>

#include "appkit/singleton.h"
#include "appkit/strutil.h"
#include "appkit/thread.h"
#include "appkit/tracer.h"

namespace appkit {

class SignalHandler : public Singleton<SignalHandler> {
    DECL_CLASSMETA(SignalHandler)
    DECL_SINGLETON(SignalHandler)

public:
    using Handler = std::function<void()>;

public:
    bool registerHandler(int signo, const Handler &handler) {
        std::lock_guard<std::mutex> lock(m_handlerMutex);
        auto iter = m_sigActionMap.find(signo);
        if (iter == m_sigActionMap.end()) {
            auto sa = std::make_shared<SigAction>(signo);
            if (!sa->addHandler(handler)) {
                return false;
            }
            m_sigActionMap.insert({signo, sa});
            m_sigCountMap.insert({signo, 0});
        } else {
            iter->second->addHandler(handler);
        }
        return true;
    }

    void checkSignal() {
        for (auto &sigCount : m_sigCountMap) {
            auto signo = sigCount.first;
            if (m_sigCountMap[signo] > 0) {
                std::lock_guard<std::mutex> lock(m_handlerMutex);
                auto iter = m_sigActionMap.find(signo);
                if (iter == m_sigActionMap.end()) {
                    continue;
                }
                TRACE_WARN_CLASS("recv signal: %d", signo);
                iter->second->handle();
                m_sigCountMap[signo] = 0;
            }
        }
    }

private:
    void addSignal(int signo) {
        auto iter = m_sigCountMap.find(signo);
        if (iter != m_sigCountMap.end()) {
            m_sigCountMap[signo] = iter->second + 1;
        }
    }
    struct SigAction {
        using Ptr = std::shared_ptr<SigAction>;
        explicit SigAction(int signo) : m_signo(signo) {}
        bool addHandler(const Handler &handler) {
            if (!m_hooked) {
                struct sigaction action;
                memset(&action, 0, sizeof(action));
                sigemptyset(&action.sa_mask);
                action.sa_sigaction = &SigAction::callback;
                action.sa_flags = SA_SIGINFO;
                ssize_t ret = sigaction(m_signo, &action, &m_action);
                if (ret < 0) {
                    return false;
                }
                m_hooked = true;
            }
            m_handlers.emplace_back(handler);
            return true;
        }

        static void callback(int signo, siginfo_t *siginfo, void *context) {
            SignalHandler::getInstance().addSignal(signo);
            signal(signo, SIG_DFL);
        }
        void handle() {
            for (auto &handler : m_handlers) {
                handler();
            }
        }
        int m_signo{-1};
        bool m_hooked{false};
        struct sigaction m_action;
        std::vector<Handler> m_handlers;
    };
    friend class SigAction;
    std::mutex m_handlerMutex;
    std::map<int, int> m_sigCountMap;
    std::map<int, SigAction::Ptr> m_sigActionMap;
};

SignalHandler::SignalHandler() {}

bool Component::addTask(const AsyncTaskQueue::Task &task) {
    if (m_context) {
        return m_context->addTask(task);
    }
    return false;
}

bool Component::isRunning() { return m_runFlag.load(); }

Application::Application() {}

Application::~Application() {}

bool Application::addComponent(const std::string &name, Component::Ptr comp) {
    std::lock_guard<std::mutex> lock(m_componentsMutex);
    if (!comp) {
        return false;
    }
    comp->m_context = this;
    if (findComponent(name) >= 0) {
        return false;
    }
    if (!comp->onInit()) {
        return false;
    }
    comp->setName(name);
    m_components.push_back({name, comp});
    return true;
}

bool Application::removeComponent(const std::string &name) {
    std::lock_guard<std::mutex> lock(m_componentsMutex);
    auto idx = findComponent(name);
    if (idx < 0) {
        return true;
    }
    if (m_components[idx].second->m_runFlag.load()) {
        m_components[idx].second->onStop();
        m_components[idx].second->m_runFlag.store(false);
    }
    if (!m_components[idx].second->onDeinit()) {
        return false;
    }
    m_components.erase(idx);
    return true;
}

bool Application::addTask(const AsyncTaskQueue::Task &task) {
    if (m_taskQueue) {
        m_taskQueue->putTask(task);
        return true;
    }
    TRACE_ERR_CLASS("cannot add task before application startup!");
    return false;
}

bool Application::registerSignal(int signo, std::function<void()> handler) {
    return SignalHandler::getInstance().registerHandler(signo, handler);
}

bool Application::startup(int workers) {
    if (!m_taskQueue) {
        m_taskQueue = std::make_shared<AsyncTaskQueue>("Application");
        if (!m_taskQueue->init(workers)) {
            TRACE_ERR_CLASS("task queue init error!");
            return false;
        }
    }
    int i = 0;
    for (i = 0; i < m_components.size(); i++) {
        m_components[i].second->m_runFlag.store(true);
        if (!m_components[i].second->onStart()) {
            m_components[i].second->m_runFlag.store(false);
            TRACE_ERR_CLASS("Component[%s] start error!",
                            m_components[i].first.data());
            break;
        }
    }
    if (i != m_components.size()) {
        for (auto j = 0; j < i; j++) {
            m_components[j].second->onStop();
            m_components[i].second->m_runFlag.store(false);
        }
        return false;
    }
    return m_thread.start(*this);
}

void Application::quit() {
    for (auto i = 0; i < m_components.size(); ++i) {
        if (!m_components[i].second->onStop()) {
            TRACE_ERR_CLASS("Component[%s] stop error!",
                            m_components[i].first.data());
        }
        m_components[i].second->m_runFlag.store(false);
    }
    m_thread.stop();
    Tracer::getInstance().flush();
}

void Application::run(const Thread &thread) {
    while (thread.isRunning()) {
        SignalHandler::getInstance().checkSignal();
        Thread::msleep(10);
    }
}

int Application::findComponent(const std::string &name) {
    for (auto i = 0; i < m_components.size(); ++i) {
        if (m_components[i].first == name) {
            return i;
        }
    }
    return -1;
}
}  // namespace appkit
